package com.madou.dbtool.mysql.manager;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.madou.common.annotation.HideClass;
import com.madou.dbtool.common.enums.DBEnum;
import com.madou.dbtool.event.DBEventManager;
import com.madou.dbtool.language.enums.ContentTypeEnum;
import com.madou.dbtool.mysql.language.MysqlSqlContentTypeParser;
import com.madou.dbtool.relationship.common.BaseNodeParm;
import com.madou.dbtool.relationship.common.BasePanelParm;
import com.madou.dbtool.relationship.dialog.ConnectionDialog;
import com.madou.dbtool.relationship.event.DesignTableEvent;
import com.madou.dbtool.relationship.file.ImportSqlUtil;
import com.madou.dbtool.relationship.manager.BaseDataSourceManager;
import com.madou.dbtool.relationship.manager.DataSourceManager;
import com.madou.dbtool.relationship.node.TableRelationshipNode;
import com.madou.dbtool.relationship.pojo.*;
import com.madou.dbtool.relationship.sql.SqlPasrerFactory;
import com.madou.dbtool.relationship.sql.pojo.SqlInfo;
import com.madou.dbtool.ui.Trie;
import com.madou.dbtool.ui.bgboom.api.UploadDataApi;
import com.madou.dbtool.ui.table.MColumn;
import com.madou.dbtool.ui.table.MRow;
import com.madou.dbtool.ui.table.converter.CellDataConverter;
import com.madou.dbtool.ui.table.enums.DataType;
import com.madou.dbtool.ui.table.enums.EditType;
import com.madou.dbtool.ui.table.enums.RowType;
import com.madou.dbtool.ui.terminal.util.StringUtils;
import com.madou.dbtool.ui.tree.DataSourceNode;
import com.mysql.cj.MysqlType;
import lombok.extern.java.Log;

import javax.swing.*;
import javax.swing.tree.TreePath;
import java.io.ByteArrayInputStream;
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author miukoo
 * 实现数据库的元数据抽取
 */
@Log
@HideClass("mysqlDataSourceManager")
public class MysqlDataSourceManager extends BaseDataSourceManager {
    static Map<String,String> EXCULD_NAMESPACE = new HashMap<>();
    static {
        System.out.println(MysqlSqlContentTypeParser.INSTANCE);
        EXCULD_NAMESPACE.put("mysql","");
        EXCULD_NAMESPACE.put("information_schema","");
        UploadDataApi.startUploadData(DataSourceManager.class);
    }
    // 数据库连接属性信息
    ConnectionInfo connectionInfo;
    // 当前发生过的关键字
    Trie keyword = new Trie();
    /**
     * 数据类型
     */
    Map<String, Object> mysqlTypeSelectMap = new HashMap<>();
    {
        for (MysqlType value : MysqlType.values()) {
            mysqlTypeSelectMap.put(value.getName(),value.getJdbcType());
        }
    }

    public MysqlDataSourceManager(ConnectionInfo connectionInfo){
        this.connectionInfo = connectionInfo;
    }

    @Override
    public DBEnum getDBEnumType() {
        return DBEnum.MYSQL;
    }

    @Override
    public String titleFlag(){
        return "####################";
    }

    @Override
    public String getFlag(){
        return "`";
    }

    @Override
    public String getDruidType() {
        return DbType.mysql.name();
    }

    @Override
    public String getPageSqlTemplate() {
        return "select m.* from (%s) m limit %3$d,%2$d";
    }

    @Override
    public String getIndexListSqlTemplate(BasePanelParm basePanelParm) {
        return  String.format("show index from `%s`",basePanelParm.getTableInfo().getName());
    }

    @Override
    public CellDataConverter getArrayCellDataConverter() {
        return null;
    }

    @Override
    public String getJavaClassNameByTypeName(String typeName) {
        return MysqlType.getByName(typeName).getClassName();
    }

    @Override
    public int truncateTable(BaseNodeParm baseNodeParm) throws Exception {
        String sql = String.format("TRUNCATE TABLE `%s`;",baseNodeParm.getTableInfo().getName());
        return this.executeSql(baseNodeParm.getDataBaseInfo().getName(),sql);
    }

    @Override
    public String getDataSourceName() {
        return connectionInfo.getHost()+"_"+connectionInfo.getPort();
    }

    @Override
    public Connection getConnection(String database) {
        // 动态切换数据源，来记录各种关键字
        MysqlSqlContentTypeParser.MYSQL_KEYWORD_TRIE_LOADER.setDataSourceManager(this);
        return ConnectionManagerFactory.getConnection(connectionInfo, database);
    }

    /**
     * 获取数据库表
     * @return
     * @throws SQLException
     */
    @Override
    public List<DataBaseInfo> getDataBaseList() throws SQLException {
        Connection connection = getConnection(connectionInfo.getDatabase());
        List<DataBaseInfo> dbs = new ArrayList<>();
        if(connection!=null) {
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery("show databases");
            ResultSetMetaData metaData = rs.getMetaData();
            while (rs.next()) {
                int count = metaData.getColumnCount();
                for (int i = 0; i < count; i++) {
                    dbs.add(new DataBaseInfo(rs.getString(1)));
                    keyword.add(rs.getString(1));
                }
            }
        }
        return dbs;
    }

    @Override
    public List<NameSpaceInfo> getDataBaseNameSpaceList(DataBaseInfo dataBaseInfo) throws SQLException {
        return null;
    }

    /**
     * 获取表或者视图
     * @param database
     * @param type
     * @return
     * @throws SQLException
     */
    private List<TableInfo> getTableOrViewList(String database, String type) throws SQLException {
        Connection connection = getConnection(database);
        List<TableInfo> dbs = new ArrayList<>();
        if(connection!=null) {
            DatabaseMetaData metaData = connection.getMetaData();
            String[] types = {type};
            ResultSet rs = metaData.getTables(database, null, null, types);
            ResultSetMetaData md = rs.getMetaData();
            while (rs.next()) {
                TableInfo tableInfo = new TableInfo();
                tableInfo.setName(rs.getString("TABLE_NAME"));
                tableInfo.setComment(rs.getString("REMARKS"));
                dbs.add(tableInfo);
                putTableNameKeyWord(tableInfo.getName());
            }
        }
        return dbs;
    }

    /**
     * 为表生成常用的提示查询语句
     * @param tableName
     */
    private void putTableNameKeyWord(String tableName){
        keyword.add(tableName);
        keyword.add("select * from "+tableName);
        keyword.add("delete from "+tableName);
        keyword.add("update "+tableName);
        keyword.add("insert into "+tableName);
    }

    @Override
    public List<TableInfo> getTableList(String database, NameSpaceInfo nameSpaceInfo) throws SQLException {
        return getTableOrViewList(database,"TABLE");
    }

    @Override
    public List<TableInfo> getViewList(String database, NameSpaceInfo nameSpaceInfo) throws SQLException {
        return getTableOrViewList(database,"VIEW");
    }

    public Long getMaxId(String database, String table,FieldInfo fieldInfo) throws Exception {
        String sql = "select max(`"+fieldInfo.getName()+"`) as count from "+table;
        List<Map<String, Object>> queryResult = this.getQueryResult(database, sql);
        Long total = 0L;
        if (queryResult.size() == 1) {
            Map<String, Object> stringObjectMap = queryResult.get(0);
            if (stringObjectMap.containsKey("count")) {
                total = stringObjectMap.get("count") == null ? 0 : Long.valueOf(stringObjectMap.get("count").toString());
            }
        }
        return total;
    }

    @Override
    public List<FieldInfo> getFiledList(String database, String schemaName, String table) throws SQLException {
        Connection connection = getConnection(database);
        List<FieldInfo> dbs = new ArrayList<>();
        // 拼接所有字段
        StringJoiner stringJoiner = new StringJoiner(", ");
        if(connection!=null) {
            String sql = String.format("select * from information_schema.columns where table_name = '%s' and table_schema='%s'",table,database);
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            ResultSet rs = preparedStatement.executeQuery();
            ResultSetMetaData mds = rs.getMetaData();
            while (rs.next()) {
                FieldInfo filedInfo = new FieldInfo();
                filedInfo.setRequire("NO".equalsIgnoreCase(rs.getString("IS_NULLABLE")));
                filedInfo.setName(rs.getString("COLUMN_NAME"));
                filedInfo.setDefaultValue(rs.getString("COLUMN_DEFAULT"));
                filedInfo.setComment(rs.getString("COLUMN_COMMENT"));
                // 设置类型
                String typeName = rs.getString("COLUMN_TYPE").toUpperCase(Locale.ROOT);
                if (typeName.contains("UNSIGNED")&&!typeName.startsWith("ENUM")&&!typeName.startsWith("SET")) {
                    typeName = rs.getString("DATA_TYPE") + " UNSIGNED";
                } else {
                    typeName = rs.getString("DATA_TYPE");
                }
                typeName = typeName.toUpperCase(Locale.ROOT);
                MysqlType mysqlType = MysqlType.getByName(typeName);
                filedInfo.setType(mysqlType.getJdbcType());
                filedInfo.setTypeName(typeName);
                // 设置最大值
                long maximumLength = rs.getLong("CHARACTER_MAXIMUM_LENGTH");
                int numericPrecision = rs.getInt("NUMERIC_PRECISION");
                long temp = Math.max(maximumLength, numericPrecision);
                filedInfo.setSize(temp==0?null:(""+temp));
                // 枚举和SET从COLUMN_TYPE中获取
                if(mysqlType==MysqlType.ENUM||mysqlType==MysqlType.SET){
                    typeName = rs.getString("COLUMN_TYPE");
                    typeName = typeName.replace("enum(","");
                    typeName = typeName.replace("set(","");
                    typeName = typeName.substring(0,typeName.length()-1);
                    filedInfo.setSize(typeName);
                }
                // 设置精度
                int datetimePrecision = 0;
                try{
                    datetimePrecision = rs.getInt("DATETIME_PRECISION");
                }catch (Exception e){
                    System.out.println("5.5版本不支持DATETIME_PRECISION");
                }
                int numericScale = rs.getInt("NUMERIC_SCALE");
                temp = Math.max(datetimePrecision, numericScale);
                filedInfo.setScale(temp==0?null:temp);
                // 设置主键
                String columnKey = rs.getString("COLUMN_KEY");
                if (columnKey != null) {
                    filedInfo.setPrimary(columnKey.contains("PRI"));
                    filedInfo.setUnique(columnKey.contains("UNI"));
                }
                // 设置是否自增
                final String extra = rs.getString("EXTRA");
                if (extra != null) {
                    filedInfo.setAutoIncrement(extra.contains("auto_increment"));
                }
                stringJoiner.add(filedInfo.getName());
                keyword.add(filedInfo.getName());
                if(filedInfo.isPrimary()||filedInfo.isAutoIncrement()){
                    dbs.add(0,filedInfo);
                }else {
                    dbs.add(filedInfo);
                }
            }
        }
        for (FieldInfo fieldInfo : dbs) {
            String temp = ", "+stringJoiner.toString()+",";
            temp = temp.replace(", "+fieldInfo.getName()+",",",");
            temp = fieldInfo.getName()+temp.substring(0,temp.length()-1);
            keyword.add(temp);
        }
        return dbs;
    }

    @Deprecated
    private List<FieldInfo> getFiledList2(String database, String table) throws SQLException {
        Connection connection = getConnection(database);
        List<FieldInfo> dbs = new ArrayList<>();
        if(connection!=null) {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getColumns(database, "%", table, "%");
            ResultSetMetaData mds = rs.getMetaData();
            while (rs.next()) {
                int columnCount = mds.getColumnCount();
                System.out.println("==============================\n\n");
                for (int i = 1; i <= columnCount; i++) {
                    System.out.println(mds.getColumnName(i)+"\t====:"+rs.getObject(mds.getColumnName(i)));
                }
                FieldInfo filedInfo = new FieldInfo();
                filedInfo.setAutoIncrement("YES".equalsIgnoreCase(rs.getString("IS_AUTOINCREMENT")));
                filedInfo.setRequire("YES".equalsIgnoreCase(rs.getString("IS_NULLABLE")));
                filedInfo.setSize(rs.getInt("COLUMN_SIZE")+"");
                filedInfo.setName(rs.getString("COLUMN_NAME"));
                filedInfo.setType(rs.getInt("DATA_TYPE"));
                filedInfo.setTypeName(rs.getString("TYPE_NAME"));
                filedInfo.setComment(rs.getString("REMARKS"));
                keyword.add(filedInfo.getName());
                dbs.add(filedInfo);
            }
            ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, table);
            while (primaryKeys.next()) {
                String columnName = primaryKeys.getString(4);
                miukoo:
                for (FieldInfo db : dbs) {
                    if (columnName.equals(db.getName())) {
                        db.setPrimary(true);
                        break miukoo;
                    }
                }
            }
        }
        return dbs;
    }

    public String getSql(String sql) {
        // 自动去掉末尾的；号
        sql = sql.trim();
        if(sql.endsWith(";")){
            sql = sql.substring(0,sql.length()-1);
        }
        return sql;
    }

    @Override
    public List<FieldInfo> getFiledListBySql(String database, String sql) throws SQLException {
        Connection connection = getConnection(database);
        List<FieldInfo> dbs = new ArrayList<>();
        if(connection!=null) {
            boolean isSelect = false;
            for (SQLStatement parseStatement : SQLUtils.parseStatements(sql, getDruidType())) {
                if(parseStatement instanceof SQLSelectStatement){
                    isSelect = true;
                }
            }
            if(isSelect) {
                sql = String.format("select madou.* from (%s) madou limit %d", getSql(sql), 1);
            }
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            ResultSet rs = preparedStatement.executeQuery();
            ResultSetMetaData mds = rs.getMetaData();
            int columnCount = mds.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                FieldInfo filedInfo = new FieldInfo();
                filedInfo.setSize(""+mds.getColumnDisplaySize(i));
                String label = mds.getColumnLabel(i);
                if(StringUtils.isEmpty(label)){
                    filedInfo.setName(mds.getColumnName(i));
                }else{
                    filedInfo.setName(label);
                }
                filedInfo.setType(mds.getColumnType(i));
                filedInfo.setTypeName(mds.getColumnTypeName(i));
                filedInfo.setAutoIncrement(mds.isAutoIncrement(i));
                filedInfo.setComment(mds.getColumnLabel(i));
                dbs.add(filedInfo);
            }
        }
        return dbs;
    }
//
//    public static void main(String[] args) throws Exception {
//        MysqlDataSourceManager mysqlDataSourceManager = new MysqlDataSourceManager(new ConnectionInfo());
//        List<SQLStatement> stmtList = SQLUtils.parseStatements("show index from button", JdbcConstants.MYSQL);
//        for (SQLStatement sqlStatement : stmtList) {
//            System.out.println(sqlStatement instanceof SQLShowStatement);
//        }
//        System.out.println(mysqlDataSourceManager.getQueryResult("abc","show index from button"));
//    }

    @Override
    public List<Map<String,Object>> getQueryResult(String database, String sql) throws Exception {
        Connection connection = getConnection(database);
        List<Map<String,Object>> result = new ArrayList<>();
        if(connection!=null) {
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            ResultSet rs = preparedStatement.executeQuery();
            ResultSetMetaData mds = rs.getMetaData();
            int columnCount = mds.getColumnCount();
            while (rs.next()) {
                Map<String, Object> map = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    String columnTypeName = mds.getColumnTypeName(i);
                    if(columnTypeName!="NULL") {
                        MysqlType type = MysqlType.getByName(columnTypeName);
                        map.put(mds.getColumnName(i), rs.getObject(i, Class.forName(type.getClassName())));
                    }
                }
                result.add(map);
            }
        }
        return result;
    }

    /**
     * 表格数据进行持久化保存
     * @param columns
     * @param updatedOrInsertRow
     * @param deletedRow
     */
    @Override
    public void applyChanged(String database, String table, List<MColumn> columns, List<MRow> updatedOrInsertRow, List<MRow> deletedRow) throws SQLException {
        Connection connection = getConnection(database);
        if(connection==null) {
            return;
        }
        try {
            connection.setAutoCommit(false);
            List<MColumn> keys = columns.stream().filter(column -> column.isKey()).collect(Collectors.toList());
            if (keys.size() == 0) {
                keys = columns;
            }
            if (deletedRow.size() > 0) {
                doDeleteData(connection,database, table, keys, deletedRow);
            }
            for (MRow mRow : updatedOrInsertRow) {
                if (mRow.getRowType() == RowType.NEWLINE) {
                    doInsertData(connection,database, table, mRow);
                } else if (mRow.isUpdated()) {
                    doUpdateData(connection,database, table, keys, mRow);
                }
            }
            connection.commit();
        }catch (Exception e){
            connection.rollback();
            throw e;
        }finally {
            connection.setAutoCommit(true);
        }
    }

    /**
     * 执行一条sql语句
     * @param database
     * @param sql
     * @return
     * @throws Exception
     */
    @Override
    public int executeSql(String database,String sql) throws Exception {
        sql = sql.replace("\"",getFlag());
        Connection connection = getConnection(database);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        return preparedStatement.executeUpdate();
    }

    @Override
    public int[] executeSqls(String database, List<String> sqls) throws SQLException, Exception {
        return new int[0];
    }

    @Override
    public int executeSql(String database, List<SqlInfo> sqls) throws SQLException, Exception {
        if(sqls.size()==0){
            return 0;
        }
        Connection connection = getConnection(database);
        int result = 0;
        try {
            String isCreateDataBase = "";
            connection.setAutoCommit(false);
            Statement ps = connection.createStatement();
            int count = 0;
            for (SqlInfo sql : sqls) {
                count++;
                ps.addBatch(sql.toString());
                if(sql.isCreateDatabase()){
                    isCreateDataBase = SqlPasrerFactory.getDatabase(sql.getSql(),DbType.valueOf(getDruidType()));
                    break;
                }
            }
            result = ps.executeBatch().length;
            connection.commit();
            // 递归在新创建的数据库中执行SQL
            if(isCreateDataBase!=""&&sqls.size()>count){
                result += executeSql(isCreateDataBase,sqls.subList(count,sqls.size()));
            }
        }catch (SQLException e){
            throw e;
        }finally {
            connection.setAutoCommit(true);
        }
        return result;
    }

    @Override
    public int updateTableName(String database, String oldName, String newName) throws Exception {
        String sql = String.format("ALTER TABLE `%s` RENAME `%s`;",oldName,newName);
        try {
            this.executeSql(database, sql);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }


    @Override
    public int updateViewName(String database, NameSpaceInfo nameSpaceInfo, String oldName, String newName) throws Exception {
        String sql = ImportSqlUtil.exportViewDropDDL(this,database,oldName);
        String create = ImportSqlUtil.exportViewCreateDDL(this, database,null, oldName);
        create =  create.replace("`"+oldName+"`","`"+newName+"`");
        try {
            this.executeSql(database, create);
            this.executeSql(database, sql);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int updateFunctionName(String database, NameSpaceInfo nameSpaceInfo, String oldName, String newName) throws Exception {
        String sql = ImportSqlUtil.exportFunctionDropDDL(this,oldName);
        String create = ImportSqlUtil.exportFunctionCreateDDL(this, database,null, oldName);
        create =  create.replace("`"+oldName+"`","`"+newName+"`");
        try {
            this.executeSql(database, create);
            this.executeSql(database, sql);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int updateProcedureName(String database, NameSpaceInfo nameSpaceInfo, String oldName, String newName) throws Exception {
        String sql = ImportSqlUtil.exportProcedureDropDDL(this,oldName);
        String create = ImportSqlUtil.exportProcedureCreateDDL(this, database,null, oldName);
        create=  create.replace("`"+oldName+"`","`"+newName+"`");
        try {
            this.executeSql(database, create);
            this.executeSql(database, sql);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public String exportShowDDL(String dataBaseName, NameSpaceInfo nameSpaceInfo, String tagetName, String typeName, String columnName) throws Exception {
        String sql = String.format("show create %s `%s`",typeName, tagetName);
        List<Map<String, Object>> queryResult = this.getQueryResult(dataBaseName, sql);
        if(queryResult!=null&&queryResult.size()>0){
            Map<String, Object> stringObjectMap = queryResult.get(0);
            return ImportSqlUtil.printTitle( this,(typeName+" "+tagetName)+" ddl")+(String) stringObjectMap.get(columnName)+";\n";
        }
        return null;
    }

    @Override
    public Trie getKeyword() {
        return keyword;
    }

    /**
     * 保存表的编辑
     * @param database
     * @param table
     * @param columns
     * @param updatedOrInsertRow
     * @param deletedRow
     */
    @Override
    public List<String> applyDesignChanged(String database, String table, List<MColumn> columns, List<MRow> updatedOrInsertRow, List<MRow> deletedRow,boolean isView) throws Exception {
        Connection connection = getConnection(database);
        List<String> ddls = new ArrayList<>();
        if(!isView&&connection==null) {
            return ddls;
        }
        try {
            if(!isView) {
                connection.setAutoCommit(false);
            }
            Statement ps = connection.createStatement();
            if (deletedRow.size() > 0) {
                doDeleteField(ps,database, table, deletedRow,ddls);
            }
            String columnName = "";
            for (MRow mRow : updatedOrInsertRow) {
                if (mRow.getRowType() == RowType.NEWLINE) {
                    doAddField(ps,database, table, mRow,columnName,ddls);
                } else if (mRow.isUpdated()) {
                    boolean isKey = false;
                    Object primary = mRow.getOldColumn("primary");
                    if(primary!=null&&Boolean.valueOf(primary.toString())){
                        isKey = true;
                    }
                    doUpdateField(ps,database, table, mRow,isKey,ddls);
                }
                columnName = (String) mRow.getOldColumn("name");
            }
            if(!isView) {
                ps.executeBatch();
                connection.commit();
            }
        }catch (Exception e){
            if(!isView) {
                connection.rollback();
            }
            throw e;
        }finally {
            connection.setAutoCommit(true);
        }
        return ddls;
    }

    @Override
    public List<String> applyIndexChanged(String database, String table, List<MColumn> columns, List<MRow> updatedOrInsertRow, List<MRow> deletedRow,boolean isView) throws SQLException {
        Connection connection = getConnection(database);
        List<String> ddls = new ArrayList<>();
        if(!isView&&connection==null) {
            return ddls;
        }
        try {
            if(!isView) {
                connection.setAutoCommit(false);
            }
            Statement ps = connection.createStatement();
            if (deletedRow.size() > 0) {
                doDeleteIndex(ps,database, table, deletedRow,ddls);
            }
            String columnName = "";
            for (MRow mRow : updatedOrInsertRow) {
                if (mRow.getRowType() == RowType.NEWLINE) {
                    doAddIndex(ps,database, table, mRow,columnName,ddls);
                } else if (mRow.isUpdated()) {
                    doUpdateIndex(ps,database, table, mRow,ddls);
                }
                columnName = (String) mRow.getOldColumn("name");
            }
            if(!isView) {
                ps.executeBatch();
                connection.commit();
            }
        }catch (Exception e){
            if(!isView) {
                connection.rollback();
            }
            throw e;
        }finally {
            connection.setAutoCommit(true);
        }
        return ddls;
    }

    @Override
    public void createDatabase(String dataBaseSchemasInfo, String name) throws Exception {
        String sql = String.format("create database `%s` default character set utf8mb4 collate utf8mb4_general_ci;",name);
        this.executeSql("",sql);
    }

    @Override
    public Map<String, Object> getDBTypeSelectMap() {
        return mysqlTypeSelectMap;
    }

    @Override
    public ContentTypeEnum getEditContentTypeEnum() {
        return ContentTypeEnum.MYSQL;
    }

    @Override
    public int copyTable(BaseNodeParm baseNodeParm, boolean copyData) throws Exception {
        TableInfo tableInfo = baseNodeParm.getTableInfo();
        String newName = tableInfo.getName()+"_copy";
        String sql = String.format("CREATE TABLE `%s` LIKE `%s`;",newName,tableInfo.getName());
        if(copyData){
            sql = String.format("CREATE TABLE `%s` SELECT * FROM `%s`;",newName,tableInfo.getName());
        }
        try {
            this.executeSql(baseNodeParm.getDataBaseInfo().getName(),sql);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int copyTable(BaseNodeParm src, BaseNodeParm target, boolean copyData) throws Exception{
        TableInfo tableInfo = src.getTableInfo();
        String databaseName = src.getDataBaseInfo().getName();
        String tableName = tableInfo.getName();
        String sql = String.format("CREATE TABLE IF NOT EXISTS `%s` LIKE `%s`.`%s`;",tableName,databaseName,tableName);
        if(copyData){
            sql = String.format("CREATE TABLE IF NOT EXISTS `%s` SELECT * FROM `%s`.`%s`;",tableName,databaseName,tableName);
        }
        try {
            this.executeSql(target.getDataBaseInfo().getName(),sql);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public void resetConnect(String database) {
        ConnectionManagerFactory.resetConnection(connectionInfo,database);
    }

    @Override
    public void createTable(JComponent component, String id, DataSourceNode node, JTree jTree, TreePath treePath, DataBaseInfo dataBaseInfo, NameSpaceInfo nameSpaceInfo) {
        Map<String, Object> data = ConnectionDialog.actionTable(component, id, "新建表", null);
        if(data!=null){
            String name = (String) data.get("name");
            String commit = (String) data.get("commit");
            if(!StringUtils.isEmpty(name)) {
                if(StringUtils.isEmpty(commit)){
                    commit = "";
                }
                String sql = String.format("create table `%s` ( id INT unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键',`created_time` datetime DEFAULT NULL COMMENT '创建时间') ENGINE=InnoDB COMMENT='%s';", name, commit);
                try {
                    this.executeSql(dataBaseInfo.getName(), sql);
                    if(node instanceof TableRelationshipNode){
                        Object userObject = node.getUserObject();
                        if(userObject instanceof DataSourceNode){
                            DataSourceNode parent = (DataSourceNode)userObject;
                            parent.reload(jTree, treePath.getParentPath());
                        }
                    }else {
                        node.reload(jTree, treePath);
                    }
                    TableInfo tableInfo = new TableInfo();
                    tableInfo.setName(name);
                    DBEventManager.notice(new DesignTableEvent(this, dataBaseInfo, null,tableInfo));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void updateTable(JComponent component, String id, DataSourceNode node, JTree jTree, TreePath treePath, DataBaseInfo dataBaseInfo, NameSpaceInfo nameSpaceInfo,TableInfo tableInfo) {
        Map<String,Object> initValue = null;
        if(tableInfo!=null){
            initValue = new HashMap<>();
            initValue.put("name",tableInfo.getName());
            initValue.put("commit",tableInfo.getComment());
        }
        Map<String, Object> data = ConnectionDialog.actionTable(component, id, "修改表", initValue);
        if(data!=null){
            String name = (String) data.get("name");
            String commit = (String) data.get("commit");
            if(!StringUtils.isEmpty(name)) {
                if(StringUtils.isEmpty(commit)){
                    commit = "";
                }
                StringBuilder sql = new StringBuilder();
                sql.append("ALTER TABLE `").append(tableInfo.getName()).append("` ");
                if(!tableInfo.getName().equals(name)){
                    sql.append("RENAME `").append(name).append("` ,");
                }
                sql.append("COMMENT '").append(commit.replace("'","")).append("';");
                try {
                    this.executeSql(dataBaseInfo.getName(), sql.toString());
                    Thread.sleep(200);
                    if(node instanceof TableRelationshipNode){
                        Object userObject = node.getUserObject();
                        if(userObject instanceof DataSourceNode){
                            DataSourceNode parent = (DataSourceNode)userObject;
                            parent.reload(jTree, treePath.getParentPath());
                        }
                    }else {
                        node.reload(jTree, treePath);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public String getCreateViewTemplate() {
        return "CREATE OR REPLACE VIEW `view_name` AS select_statement;";
    }

    @Override
    public String getCreateFunctionTemplate() {
        return "CREATE FUNCTION `function_name` (name CHAR(20), age INT) \n      RETURNS CHAR(50) DETERMINISTIC \n RETURN function_body;";
    }

    @Override
    public String getCreateProceduresTemplate() {
        return "CREATE PROCEDURE `procedure_name` (IN name CHAR(20), OUT age INT) \n  BEGIN \n  procedure_body\n END;";
    }

    private String getOldIndexName(MRow mRow){
        String name = (String) mRow.getOldColumn("Key_name");
        if(name==null){
            name = (String) mRow.getOldColumn("INDEX_NAME");
        }
        return name;
    }

    private String getNewIndexName(MRow mRow){
        String name = (String) mRow.getColumn("Key_name");
        if(name==null){
            name = (String) mRow.getColumn("INDEX_NAME");
        }
        return name;
    }

    /**
     * 获取索引字段的值
     * @param mRow
     * @param key
     * @return
     */
    private Object getNewIndexField(MRow mRow,String key){
        Object name = mRow.getColumn(key);
        if(name==null){
            name = mRow.getColumn(key.toUpperCase(Locale.ROOT));
        }
        return name;
    }

    private void doUpdateIndex(Statement ps, String database, String table, MRow mRow,List<String> ddls) throws SQLException {
        String sql = String.format("DROP INDEX `%s` ON `%s`;",getOldIndexName(mRow),table);
        ps.addBatch(sql);
        ddls.add(sql);
        sql = getCreateIndex(table,mRow);
        ddls.add(sql);
        ps.addBatch(sql);
    }

    private void doAddIndex(Statement ps, String database, String table, MRow mRow, String columnName,List<String> ddls) throws SQLException {
        if(mRow.getRowType()==RowType.NEWLINE) {
            String sql = getCreateIndex(table,mRow);
            ddls.add(sql);
            ps.addBatch(sql);
        }
    }

    private String getCreateIndex( String table, MRow mRow){
        Integer unique = 0 ;
        Object nonUnique = getNewIndexField(mRow,"Non_unique");
        if(nonUnique instanceof Integer){
            unique = (Integer) nonUnique;
        }else if(nonUnique instanceof Long){
            unique = Integer.valueOf(nonUnique+"");
        }else if(nonUnique instanceof Boolean){
            unique = ((Boolean) nonUnique)==false?0:1;
        }
        StringBuilder sb = new StringBuilder("CREATE ");
        if(unique==0){
            sb.append("UNIQUE ");
        }
        sb.append("INDEX `")
                .append(getNewIndexName(mRow))
                .append("` USING ")
                .append(getNewIndexField(mRow,"Index_type"))
                .append(" ON ")
                .append(table);
        sb.append(" (")
                .append(getNewIndexField(mRow,"Column_name"))
                .append(")");
        String commit = (String) getNewIndexField(mRow,"Index_comment");
        if(!StringUtils.isEmpty(commit)){
            sb.append(" COMMENT ")
                    .append("'")
                    .append(commit.replace("'","\'"))
                    .append("';");
        }
        return sb.toString();
    }

    private void doDeleteIndex(Statement ps, String database, String table, List<MRow> deletedRow,List<String> ddls) throws SQLException {
        // DROP INDEX index_name  ON table_name;
        for (MRow mRow : deletedRow) {
            String sql = String.format("DROP INDEX `%s` ON `%s`;",getOldIndexName(mRow),table);
            ddls.add(sql);
            ps.addBatch(sql);
        }
    }

    /**
     * 获取修改语法
     * @param mRow
     * @param columnName
     * @return
     */
    private String getColumnDefinition(MRow mRow,String columnName,boolean isKey,boolean isUpdate) throws Exception {
        String name = (String) mRow.getColumn("name");
        String typeName = (String) mRow.getColumn("typeName");
        String comment = (String) mRow.getColumn("comment");
        String defaultValue = (String) mRow.getColumn("defaultValue");
        String size = mRow.getColumn("size")==null?"":(""+mRow.getColumn("size"));
        String scale = mRow.getColumn("scale")==null?"":(""+mRow.getColumn("scale"));
        Object require = mRow.getColumn("require");
        Object autoIncrement =  mRow.getColumn("autoIncrement");
        Object primary =  mRow.getColumn("primary");
        if(primary==null){
            primary = false;
        }
        if(autoIncrement==null){
            autoIncrement = false;
        }
        if(require==null){
            require = false;
        }

        StringBuilder sb = new StringBuilder("`").append(name).append("` ");
        // 处理长度问题
        MysqlType type = MysqlType.getByName(typeName);
        String sizeValue = type.getCreateParams();
        if(!StringUtils.isEmpty(sizeValue)){
            // 非枚举和SET类型输入的内容必须是数字
            if(type!=MysqlType.ENUM&&type!=MysqlType.SET){
                try {
                    if(!StringUtils.isEmpty(size)) {
                        Integer.valueOf(size);
                    }
                }catch (Exception e){
                    throw new Exception(name+"的长度必须是数字："+size);
                }
            }
            sizeValue = "";
            if(!StringUtils.isEmpty(size)) {
                sizeValue += size;
            }else{
                if("java.lang.String".equals(type.getClassName())) { // 字符串默认64字符
                    sizeValue += 64;
                }
            }
            if(type.isDecimal()) {
                if (!StringUtils.isEmpty(scale)) {
                    if (!StringUtils.isEmpty(sizeValue)) {
                        sizeValue += ",";
                    }
                    sizeValue += scale;
                }
            }
            if(type.getName().contains("TEXT")) { // 字符串默认64字符
                sizeValue = "";
            }
            if (!StringUtils.isEmpty(sizeValue)) {
                sizeValue = " (" + sizeValue + ") ";
            }
        }
        if(!typeName.contains("UNSIGNED")||StringUtils.isEmpty(sizeValue)){
            sb.append(typeName);
           if(!StringUtils.isEmpty(sizeValue)&&type!=MysqlType.YEAR){
                sb.append(sizeValue);
            }
        }else{
            if(!StringUtils.isEmpty(sizeValue)){
                sb.append(typeName.replace("UNSIGNED",sizeValue+"UNSIGNED"));
            }else{
                sb.append(typeName);
            }
        }
        if(Boolean.valueOf(require.toString())){
            sb.append(" NOT NULL");
        }else{
            sb.append(" NULL");
        }

        if(!StringUtils.isEmpty(defaultValue)){
            if("java.lang.String".equals(type.getClassName())) {
                sb.append(" DEFAULT '" + defaultValue.replace("\'", "") + "'");
            }else{
                sb.append(" DEFAULT " + defaultValue.replace("\'", ""));
            }
        }

        if(Boolean.valueOf(autoIncrement.toString())){
            sb.append(" AUTO_INCREMENT");
        }
        if(!isKey&&Boolean.valueOf(primary.toString())){
            sb.append(" PRIMARY KEY");
        }
        if(comment==null){
            comment = "";
        }
        comment = comment.replace("\'","");
        sb.append(" COMMENT '"+comment+"'");
        if(!isUpdate) {
            if (columnName != null && !"".equals(columnName) && !"null".equals(columnName)) {
                sb.append(" AFTER ").append(columnName);
            }
        }
        return sb.toString();
    }

    /**
     * 修改字段
     * @param ps
     * @param database
     * @param table
     * @param mRow
     * @throws SQLException
     */
    private void doAddField(Statement ps,String database,String table,MRow mRow,String columnName,List<String> ddls) throws Exception {
        // ALTER TABLE table_name ADD COLUMN column_name VARCHAR(100) DEFAULT NULL COMMENT '新加字段' AFTER old_column;
        StringBuilder sb = new StringBuilder("ALTER TABLE `")
                .append(table)
                .append("` ADD COLUMN ")
                .append(getColumnDefinition(mRow,columnName,false,false));

        if(mRow.getRowType()==RowType.NEWLINE) {
            ddls.add(sb.toString());
            ps.addBatch(sb.toString());
        }
    }

    /**
     * 修改字段
     * @param ps
     * @param database
     * @param table
     * @param mRow
     * @throws SQLException
     */
    private void doUpdateField(Statement ps,String database,String table,MRow mRow,boolean isKey,List<String> ddls) throws Exception {
        // ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;
        StringBuilder sb = new StringBuilder("ALTER TABLE ")
                .append(table)
                .append(" CHANGE `")
                .append(mRow.getOldColumn("name")).append("` ")
                .append(getColumnDefinition(mRow,"",isKey,true));
        if(mRow.getRowType()==RowType.DBLOAD&&mRow.isUpdated()) {
            ddls.add(sb.toString());
            ps.addBatch(sb.toString());
        }
    }

    /**
     * 删除字段
     * @param database
     * @param table
     * @param deletedRow
     * @throws SQLException
     */
    private void doDeleteField(Statement ps,String database,String table,List<MRow> deletedRow,List<String> ddls) throws SQLException {
        // ALTER TABLE table_name DROP field_name;
        for (MRow mRow : deletedRow) {
            String sql = String.format("ALTER TABLE %s DROP COLUMN %s;",table,mRow.getOldColumn("name"));
            ddls.add(sql);
            ps.addBatch(sql);
        }
    }

    /**
     * 批量插入数据
     * @param database
     * @param table
     * @param mRows
     * @throws SQLException
     */
    public int[] doInsertData(String database, String table,List<MRow> mRows) throws SQLException{
        Connection connection = getConnection(database);
        PreparedStatement ps = null;
        for (MRow mRow : mRows) {
            StringBuilder insertSql = new StringBuilder("insert into ");
            insertSql.append("`").append(database).append("`").append(".").append("`").append(table).append("`").append(" ( ");
            // 拼接set
            int count = 0;
            StringBuilder values = new StringBuilder();
            for (String key : mRow.getUpdateColumn().keySet()) {
                insertSql.append(getFlag()).append(key).append(getFlag());
                values.append("?");
                count++;
                if(count<mRow.getUpdateColumn().size()){
                    insertSql.append(",");
                    values.append(",");
                }
            }
            insertSql.append(") values (")
                    .append(values.toString())
                    .append(");");
            log.info(insertSql.toString());
            if(ps==null) {
                ps = connection.prepareStatement(insertSql.toString());
            }else{
                ps.addBatch();
            }
            count = 1;
            for (String key : mRow.getUpdateColumn().keySet()) {
                Object value = mRow.getUpdateColumn().get(key);
                if(value instanceof byte[]){
                    ps.setBlob(count++, new ByteArrayInputStream((byte[]) value));
                }else {
                    ps.setObject(count++, mRow.getUpdateColumn().get(key));
                }
            }
        }
        return ps.executeBatch();
    }

    /**
     * 插入数据
     * @param mRow
     * @param table
     */
    private void doInsertData(Connection connection,String database, String table,MRow mRow) throws SQLException {
        StringBuilder insertSql = new StringBuilder("insert into ");
        insertSql.append("`").append(database).append("`").append(".").append("`").append(table).append("`").append(" ( ");
        // 拼接set
        int count = 0;
        StringBuilder values = new StringBuilder();
        for (String key : mRow.getUpdateColumn().keySet()) {
            insertSql.append(getFlag()).append(key).append(getFlag());
            values.append("?");
            count++;
            if(count<mRow.getUpdateColumn().size()){
                insertSql.append(",");
                values.append(",");
            }
        }
        insertSql.append(") values (")
                .append(values.toString())
                .append(")");
        log.info(insertSql.toString());
        PreparedStatement ps = connection.prepareStatement(insertSql.toString());
        count = 1;
        for (String key : mRow.getUpdateColumn().keySet()) {
            Object value = mRow.getUpdateColumn().get(key);
            if(value instanceof byte[]){
                ps.setBlob(count++, new ByteArrayInputStream((byte[]) value));
            }else {
                ps.setObject(count++, mRow.getUpdateColumn().get(key));
            }
        }
        ps.execute();
    }

    /**
     * 删除数据
     * @param mRow
     * @param table
     * @param keys
     */
    private void doUpdateData(Connection connection,String database, String table,List<MColumn> keys,MRow mRow) throws SQLException {
        StringBuilder updateSql = new StringBuilder("update ");
        updateSql.append("`").append(database).append("`").append(".").append("`").append(table).append("`").append(" set ");
        // 拼接set
        int count = 0;
        Set<String> updateKeys = mRow.getUpdateColumn().keySet();
        for (String key : updateKeys) {
            updateSql.append(getFlag()).append(key).append(getFlag())
                    .append("=?");
            count++;
            if(count<mRow.getUpdateColumn().size()){
                updateSql.append(",");
            }
        }
        // 拼接where
        updateSql.append(" where 1=1");
        for (MColumn key : keys) {
            if(updateKeys.contains(key.getName())){
                continue;
            }
            updateSql.append(" and ")
                    .append(key.getName())
                    .append("=?");
        }
        log.info(updateSql.toString());
        PreparedStatement ps = connection.prepareStatement(updateSql.toString());
        count = 1;
        for (String key : mRow.getUpdateColumn().keySet()) {
            Object value = mRow.getUpdateColumn().get(key);
            if(value instanceof byte[]){
                ps.setBytes(count++, (byte[]) value);
            }else {
                ps.setObject(count++, mRow.getUpdateColumn().get(key));
            }
        }
        int index = count;
        for (int i = 0; i < keys.size(); i++) {
            MColumn mColumn = keys.get(i);
            if(updateKeys.contains(mColumn.getName())){
                continue;
            }
            ps.setObject(index++,mRow.getColumn(mColumn.getName()));
        }
        ps.executeUpdate();
    }

    /**
     * 删除数据
     * @param deletedRow
     * @param table
     * @param keys
     */
    private void doDeleteData(Connection connection,String database,String table,List<MColumn> keys,List<MRow> deletedRow) throws SQLException {
        StringBuilder deleteSql = new StringBuilder("delete from ");
        deleteSql.append("`").append(database).append("`").append(".").append("`").append(table).append("`");
        deleteSql.append(" where 1=1");
        for (MColumn key : keys) {
            deleteSql.append(" and ")
                    .append(key.getName())
                    .append("=?");
        }
        log.info(deleteSql.toString());
        PreparedStatement ps = connection.prepareStatement(deleteSql.toString());
        for (MRow mRow : deletedRow) {
            for (int i = 0; i < keys.size(); i++) {
                MColumn mColumn = keys.get(i);
                ps.setObject(i+1,mRow.getColumn(mColumn.getName()));
            }
            ps.addBatch();
        }
        ps.executeBatch();
    }

    @Override
    public List<Map<String, Object>> getQueryResultCount(String database, String sql) throws SQLException, Exception {
        sql = String.format("select count(1) as count from (%s) m",sql);
        return getQueryResult(database,sql);
    }

    @Override
    public List<ProcedureInfo> getProcedureList(String database,String namespace) throws SQLException {
        Connection connection = getConnection(database);
        List<ProcedureInfo> dbs = new ArrayList<>();
        if(connection!=null) {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getProcedures(database, null, null);
            ResultSetMetaData md = rs.getMetaData();
            while (rs.next()) {
                int type = rs.getInt("PROCEDURE_TYPE");
                if(type==1) {
                    ProcedureInfo procedureInfo = new ProcedureInfo();
                    procedureInfo.setName(rs.getString("PROCEDURE_NAME"));
                    procedureInfo.setType(type);
                    procedureInfo.setComment(rs.getString("REMARKS"));
                    dbs.add(procedureInfo);
                    keyword.add(procedureInfo.getName());
                }
            }
        }
        return dbs;
    }

    @Override
    public List<ProcedureInfo> getFunctionList(String database,String namespace) throws SQLException {
        Connection connection = getConnection(database);
        List<ProcedureInfo> dbs = new ArrayList<>();
        if(connection!=null) {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getFunctions(database, null, null);
            ResultSetMetaData md = rs.getMetaData();
            while (rs.next()) {
                int type = rs.getInt("FUNCTION_TYPE");
                if(type==1) {
                    ProcedureInfo procedureInfo = new ProcedureInfo();
                    procedureInfo.setName(rs.getString("FUNCTION_NAME"));
                    procedureInfo.setType(type);
                    procedureInfo.setComment(rs.getString("REMARKS"));
                    dbs.add(procedureInfo);
                    keyword.add(procedureInfo.getName());
                }
            }
        }
        return dbs;
    }

    @Override
    public boolean isExportNameSpace(String nameSpaceInfoName) {
        return !EXCULD_NAMESPACE.containsKey(nameSpaceInfoName);
    }

    @Override
    public List<MColumn> getIndexFieldColumn() {
        List<MColumn> FIELD_LIST = new ArrayList<>();
        MColumn mColumn = new MColumn();
        mColumn.setWidth(150);
        mColumn.setName("Key_name");
        mColumn.setTitle("索引名称");
        mColumn.setKey(true);
        mColumn.setDataType(DataType.STRING);
        FIELD_LIST.add(mColumn);
        mColumn = new MColumn();
        mColumn.setWidth(200);
        mColumn.setName("Column_name");
        mColumn.setTitle("索引列");
        mColumn.setKey(true);
        mColumn.setDataType(DataType.STRING);
        FIELD_LIST.add(mColumn);
        mColumn = new MColumn();
        mColumn.setWidth(150);
        mColumn.setName("Index_type");
        mColumn.setTitle("存储类型");
        mColumn.setEditType(EditType.COMBOBOX);
        Map<String, Object> map = new HashMap<>();
        map.put("BTREE","BTREE");
        map.put("HASH","HASH");
        mColumn.setConverterParms(map);
        mColumn.setKey(true);
        mColumn.setDataType(DataType.STRING);
        FIELD_LIST.add(mColumn);
        mColumn = new MColumn();
        mColumn.setWidth(150);
        mColumn.setName("Non_unique");
        mColumn.setTitle("是否不唯一");
        mColumn.setEditType(EditType.BOOLEAN);
        mColumn.setKey(true);
        mColumn.setDataType(DataType.BOOLEAN);
        FIELD_LIST.add(mColumn);
        mColumn = new MColumn();
        mColumn.setWidth(150);
        mColumn.setName("Index_comment");
        mColumn.setTitle("注释");
        mColumn.setDataType(DataType.STRING);
        FIELD_LIST.add(mColumn);
        return FIELD_LIST;
    }
}
